#!/usr/bin/env perl
#
# Copyright (c) 2009 Prabhu Gurumurthy <pgurumur@gmail.com>
# 
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
# 
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
use strict;
use warnings;
use Getopt::Long;
use Socket;
use Net::SNMP qw(:snmp);

Getopt::Long::Configure("default", "bundling");
push my @prog, split(/\//, $0);
my $program = $prog[$#prog];

my $arg = int(scalar(@ARGV));
my %options = ();
my $hostname = undef;
my $timeout = 30;
my $session = undef;

my %ERRORS = (
   -1 => 'UNKNOWN',
   0  => 'OK',
   1  => 'WARNING',
   2  => 'CRITICAL', 
);

my %FEX_ROWSTATUS = (
   1 => 'active',
   2 => 'notInService',
   3 => 'notReady',
   4 => 'createAndGo',
   5 => 'createAndWait',
   6 => 'destroy',
);

my $fexlist = ".1.3.6.1.4.1.9.9.691.1.1.2.1.4";

sub main {
   GetOptions(\%options, "help|?", "hostname|H=s", "community|c=s",
      "version|v=s") or usage();
   usage() if $options{help};
   my $address = undef;

   if (!defined($options{version})) {
      $options{version} = 'snmpv2c';
   }

   if ($options{hostname} && $options{community}) {
      $SIG{INT} = \&sighandle;
      $SIG{TERM} = \&sighandle;

      my $result = gethostbyname($options{hostname});
      $hostname = $options{hostname};
      if ($result) {
         $session = Net::SNMP->session(
            -hostname      => $options{hostname},
            -community     => $options{community},
            -version       => $options{version},
            -nonblocking   => 1,
            -translate     => [-octetstring => 0],
            -version       => $options{version},
         );

         unless ($session) {
            errx(-1, "SNMP connection failed to $options{hostname}");
         }

         my %table = ();
         my $result = $session->get_bulk_request(
               -varbindlist   => [ $fexlist ],
               -callback      => [ \&fex_cb, \%table ],
               -maxrepetitions => 10,
         );

         snmp_dispatcher();

         if (defined($session)) {
            $session->close;
         }

         my $status = "";

         while (my ($key, $value) = each(%table)) {
            push my @fex, split(/\./, $key);
            if ($value != 1) {
               my $temp = sprintf("fex %s status is in %s state", $fex[$#fex],
                  $FEX_ROWSTATUS{$value});
               if (length($status) != 0) {
                  $status = sprintf("%s; %s", $status, $temp);
               }
               else {
                  $status = $temp;
               }
            }
         }

         if (length($status) == 0) {
            errx(0, "All fex switches are in active status");
         }
         else {
            errx(2, $status);
         }
      }
   }
   else {
      usage();
   }
}

sub usage {
   printf("%s options\n", $program);
   printf("options: \n");
   printf("\t-H | --hostname   [ hostname of the router ]\n");
   printf("\t-c | --commmunity [ SNMP community (default: public)]\n");
   printf("\t-v | --version    [ SNMP version (default: 1)]\n");
   printf("\t-h | --help       [ prints this help ]\n");
   printf("\n");
   printf("usage: %s -H <switch> -c <community> -v <1|2>\n", $program);
   printf("\n");
   exit 1;
}

sub errx {
   my ($state, $message) = @_;

   printf("%s: %s\n", $ERRORS{$state}, $message);
   exit($state);
}

sub fex_cb {
   my ($session, $table) = @_;
   my $list = $session->var_bind_list();
   my @names = $session->var_bind_names();
   my $next = undef;

   while (@names) {
      $next = shift @names;
      if (!oid_base_match($fexlist, $next)) {
         return;
      }

      $table->{$next} = $list->{$next};
   }

   my $result = $session->get_bulk_request(
      -varbindlist      => [ $next ],
      -maxrepetitions   => 10,
   );

   return;
}

sub sighandle {
   my ($signal) = @_;
   printf("%s got signal %s, terminating the application\n", $program, $signal);
   exit(1);
}

$SIG{'ALRM'} = sub {
   printf("ERROR: No response from %s (alarm)\n", $hostname);
   exit $ERRORS{"UNKNOWN"};
};
alarm($timeout);

if ($arg < 1) {
   usage();
}
else {
   main();
}
